﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Services;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace ADO_Data_Service
{
    public class BusinessEntities : IUpdatable
    {
        #region Initialization
        private static List<Invoice> invoiceStore;
        private static List<InvoiceLine> invoiceLinesStore;
        private static List<Thirdparty> thirdPartiesStore;
        private static List<List<InvoiceLine>> invoiceLinesCollection;

        static BusinessEntities()
        {
            invoiceLinesStore = new List<InvoiceLine>()
               {
                   new InvoiceLine(){ID = 1,Quantity = 10, UnitValue = 100},
                   new InvoiceLine(){ID = 2,Quantity = 20, UnitValue = 200},
                   new InvoiceLine(){ID = 3,Quantity = 30, UnitValue = 300},
                   new InvoiceLine(){ID = 4,Quantity = 40, UnitValue = 400},
                   new InvoiceLine(){ID = 5,Quantity = 50, UnitValue = 500},
                   new InvoiceLine(){ID = 6,Quantity = 60, UnitValue = 600},
                   new InvoiceLine(){ID = 7,Quantity = 70, UnitValue = 700},
                   new InvoiceLine(){ID = 8,Quantity = 80, UnitValue = 800},
                   new InvoiceLine(){ID = 9,Quantity = 90, UnitValue = 900},
                   new InvoiceLine(){ID = 10,Quantity = 100, UnitValue = 1000}
               };

            invoiceLinesCollection = new List<List<InvoiceLine>>()
               {
                    new List<InvoiceLine>()
                        {
                            invoiceLinesStore[0], invoiceLinesStore[1]
                        },
                   new List<InvoiceLine>()
                        {
                            invoiceLinesStore[2], invoiceLinesStore[3]
                        },
                   new List<InvoiceLine>()
                        {
                            invoiceLinesStore[4], invoiceLinesStore[5]
                        },
                   new List<InvoiceLine>()
                        {
                            invoiceLinesStore[6], invoiceLinesStore[7]
                        },
                   new List<InvoiceLine>()
                        {
                            invoiceLinesStore[8], invoiceLinesStore[9]
                        }
               };

            thirdPartiesStore = new List<Thirdparty>()
               {
                   new Thirdparty(){ID = 1, Name = "Company1"},
                   new Thirdparty(){ID = 2, Name = "Company2"},
                   new Thirdparty(){ID = 3, Name = "Company3"}
               };

            invoiceStore = new List<Invoice>()
               {                   
                   new Invoice(){dateInvoice = DateTime.Now,ID = 1,invoiceLines = invoiceLinesCollection[0], thirdParty = thirdPartiesStore[0]},
                   new Invoice(){dateInvoice = DateTime.Now,ID = 2,invoiceLines = invoiceLinesCollection[1], thirdParty = thirdPartiesStore[0]},
                   new Invoice(){dateInvoice = DateTime.Now,ID = 3,invoiceLines = invoiceLinesCollection[2], thirdParty = thirdPartiesStore[1]},
                   new Invoice(){dateInvoice = DateTime.Now,ID = 4,invoiceLines = invoiceLinesCollection[3], thirdParty = thirdPartiesStore[2]},
                   new Invoice(){dateInvoice = DateTime.Now,ID = 5,invoiceLines = invoiceLinesCollection[4], thirdParty = thirdPartiesStore[0]}
               };
        }
        #endregion

        #region IQueryable Elements
        public IQueryable<Invoice> Invoices
        {
            get { return invoiceStore.AsQueryable(); }
        }

        public IQueryable<InvoiceLine> InvoiceLines
        {
            get { return invoiceLinesStore.AsQueryable(); }
        }

        public IQueryable<Thirdparty> Thirdparties
        {
            get { return thirdPartiesStore.AsQueryable(); }
        }
        #endregion

        #region Implementation IUpdatable
        public object CreateResource(string containerName, string fullTypeName)
        {
            var t = Type.GetType(fullTypeName);
            if (t == null)
            {
                throw new ArgumentException("Type could not be identified.");
            }

            var resource = Activator.CreateInstance(t);

            //JDO: Add element to the correct entity store
            switch (containerName)
            {
                case "Invoices":
                    invoiceStore.Add((Invoice) resource);
                    break;

                case "Thirdparties":
                    thirdPartiesStore.Add((Thirdparty) resource);
                    break;

                case "InvoiceLines":
                    invoiceLinesStore.Add((InvoiceLine) resource);
                    break;

                default:
                    throw new ArgumentException("Unknown container name.");
            }
            return resource;           
        }

        public object GetResource(IQueryable query, string fullTypeName)
        {
            object resource = null;

            foreach (var o in query)
            {
                if (resource != null)
                {
                    throw new Exception("Expected a single response");
                }
                resource = o;
            }

            // fullTypeName can be null for deletes
            if (fullTypeName != null && resource!= null && resource.GetType() != Type.GetType(fullTypeName))
                throw new Exception("Unexpected type for resource");

            return resource;
        }

        public object ResetResource(object resource)
        {
            throw new NotImplementedException();
        }

        public void SetValue(object targetResource, string propertyName, object propertyValue)
        {
            var pi = targetResource.GetType().GetProperty(propertyName);
            if (pi == null)
                throw new Exception("Can't find property");

            pi.SetValue(targetResource, propertyValue, null);
        }

        public object GetValue(object targetResource, string propertyName)
        {
            var pi = targetResource.GetType().GetProperty(propertyName);
            if (pi == null)
                throw new Exception("Can't find property");

            return pi.GetValue(targetResource, null);
        }

        public void SetReference(object targetResource, string propertyName, object propertyValue)
        {
            SetValue(targetResource, propertyName, propertyValue);
        }

        public void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
        {
            var pi = targetResource.GetType().GetProperty(propertyName);
            if (pi == null)
                throw new Exception("Can't find property");

            var collection = (IList)pi.GetValue(targetResource, null);
            collection.Add(resourceToBeAdded);
        }

        public void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
        {
            var pi = targetResource.GetType().GetProperty(propertyName);
            if (pi == null)
                throw new Exception("Can't find property");
            
            var collection = (IList)pi.GetValue(targetResource, null);
            if (collection == null)
                throw new Exception("Collection is not initialized");
            
            collection.Remove(resourceToBeRemoved);
        }

        public void DeleteResource(object targetResource)
        {
            if (targetResource is Invoice)
                invoiceStore.Remove((Invoice)targetResource);

            else if (targetResource is InvoiceLine)
                invoiceLinesStore.Remove((InvoiceLine)targetResource);

            else if (targetResource is Thirdparty)
                thirdPartiesStore.Remove((Thirdparty)targetResource);

            else
                throw new Exception("Unexpected type for resource");
        }

        public void SaveChanges()
        {
            //JDO: Add some business logic
        }

        public object ResolveResource(object resource)
        {
            return resource;
        }

        public void ClearChanges()
        {
            //JDO: Add some business logic
            throw new NotImplementedException();
        }
        #endregion
    }
}
